/*
Autor: Alexander Savochkin, 2010
New BSD License
*/

#include <boost/utility/in_place_factory.hpp>
#include <boost/bind.hpp>
#include <iostream>
#include "TCPServer.h"

using std::istream;

namespace SRCLF {
namespace Server {

	void ClientConnection::writeResponce(const string& response)
	{
		boost::asio::async_write(socket, boost::asio::buffer(response.c_str(), response.length()), boost::bind( &ClientConnection::writeHandler, this, _1 ) );
	}

	void ClientConnection::writeHandler(const boost::system::error_code& error)
	{
		if (error) 
		{
			//
			errorHandler( this );
		}
	}

	void ClientConnection::receiveLineHandle(const boost::system::error_code& error)
	{
		if ( !error )
		{
			receiveHandler(this);
		}
		else
		{
			need_close = true;
			errorHandler(this);
		}
	}

	void ClientConnection::getReceivedCommand(string& received_command)
	{
			received_command.clear();
			istream received_stream(&command_buffer);
			std::copy(std::istream_iterator<char>(received_stream),
				std::istream_iterator<char>(),
				std::back_inserter(received_command));
	}

	TCPServer::TCPServer(boost::asio::io_service& _io_service, 
											   Manager& _control_loop_manager, 
											   const Configuration& config) :
		  io_service(_io_service), control_loop_manager(_control_loop_manager), timer(_io_service)
	{		
		namespace attributes = boost::log::attributes;
		boost::shared_ptr< boost::log::attribute > attr(new attributes::constant< string >("TCPServer"));
	    logger.add_attribute("module", attr);

		//Create acceptors for all tcp ports
		const set<string>& connections = control_loop_manager.getRegistredConnections();
		for (set<string>::const_iterator it = connections.begin(); it != connections.end(); ++it)
		{
			size_t delimeter_pos = it->find(":");
			if(delimeter_pos == string::npos)
				continue;
			string protocol = it->substr(0, delimeter_pos);
			if ( protocol != "tcp" )
				continue;

			string port_string = it->substr(delimeter_pos + 1);
			unsigned tcp_port = lexical_cast<unsigned>(port_string);

			//
			createAcceptor(tcp_port);
		}
	}

	void TCPServer::onAccept(size_t index, unsigned tcp_port, const boost::system::error_code& e, 
		ClientConnection* new_connection)
	{
		if (!e) {

			BOOST_LOG_SEV(logger, notification) << "Incomming new tcp connection at port " << tcp_port;			
			control_loop_manager.notifyClientConnected( new_connection->getClientInfo() );

			//Create new connection object
			new_connection->start();

			BOOST_LOG_SEV(logger, notification) << "New client id: " << new_connection->getClientInfo().getClientId();			

			new_connection = new ClientConnection(io_service, control_loop_manager.newClientId(), tcp_port, 
				boost::bind(&TCPServer::onNewCommand, this, _1), 
				boost::bind(&TCPServer::onCloseConnection, this, _1));

			//Continue accept incoming connextions
			acceptors[index]->async_accept(new_connection->getSocket(),
       				boost::bind(&TCPServer::onAccept, this, index, tcp_port, boost::asio::placeholders::error, new_connection));
		}
		else {
			BOOST_LOG_SEV(logger, warning) << "Some error in tcp accept!";
		}
	}

	void TCPServer::onCloseConnection(ClientConnection *connection)
	{
		//Notify manager
		BOOST_LOG_SEV(logger, notification) << "tcp client disconnected: client id = " << connection->getClientInfo().getClientId();			
		control_loop_manager.notifyClientDisconnected( connection->getClientInfo().getClientId() );
		delete connection;
	}

	void TCPServer::onNewCommand(ClientConnection *connection)
	{
		string incomming_command;
		BOOST_LOG_SEV(logger, notification) << "New command received from tcp client " << connection->getClientInfo().getClientId() << " cmd: " << incomming_command;
		connection->getReceivedCommand(incomming_command);
		string response;
		control_loop_manager.processCommand(connection->getClientInfo().getClientId(),
			incomming_command, response );
		connection->writeResponce(response);  //Write response
		connection->receiveNextCommand();
	}

	void TCPServer::createAcceptor(unsigned tcp_port)
	{
		tcp::acceptor * new_acceptor = new tcp::acceptor(io_service, tcp::endpoint(tcp::v4(), tcp_port));
		acceptors.push_back( shared_ptr<tcp::acceptor>(new_acceptor) );
		//Start accept connections
		ClientConnection *first_connection = new ClientConnection(io_service, control_loop_manager.newClientId(), tcp_port, 
				boost::bind(&TCPServer::onNewCommand, this, _1), 
				boost::bind(&TCPServer::onCloseConnection, this, _1));
		new_acceptor->async_accept(first_connection->getSocket(),
       		boost::bind(&TCPServer::onAccept, this, acceptors.size() - 1, tcp_port, boost::asio::placeholders::error, first_connection));
	}

} //namespace Server
} //namespace SRCLF
